package org.dreamwork.tools.code.analyzer.core;

import org.dreamwork.util.PathFilter;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;

public class ModuleWalker {
    private final Path root;

    private final Set<PathFilter> ignoredDirs = new HashSet<> ();
    private final Set<PathFilter> ignoredFiles = new HashSet<> ();

    private final Logger logger = LoggerFactory.getLogger (ModuleWalker.class);

    public ModuleWalker (Path dir) {
        this.root = dir;
    }

    public ModuleWalker setIgnoredPattern (String pattern) {
        String[] a = pattern.split (";");
        for (String p : a) {
            if (StringUtil.isEmpty (p)) continue;

            p = p.trim ().replace ('\\', '/');
            if (p.endsWith ("/")) {
                p = p.substring (0, p.length () - 1);
                ignoredDirs.add (new PathFilter (p));
            } else {
                ignoredFiles.add (new PathFilter (p));
            }
        }
        return this;
    }

    public ModuleStatisticInfo walk () throws IOException {
        ModuleStatisticInfo info = new ModuleStatisticInfo ();
        Files.walkFileTree (root, new SimpleFileVisitor<Path> () {
            @Override
            public FileVisitResult preVisitDirectory (Path path, BasicFileAttributes basicFileAttributes) {
                if (path.equals (root)) return FileVisitResult.CONTINUE;

                info.dirs ++;

                String p = root.relativize (path).toString ();
                if (!StringUtil.isEmpty (p)) {
                    p = p.replace ('\\', '/');
                }
                for (PathFilter f : ignoredDirs) {
                    if (f.hit (p)) {
                        if (logger.isTraceEnabled ()) {
                            logger.trace ("dir {} is ignored.", p);
                        }
                        info.ignoredDirs.add (path);
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile (Path path, BasicFileAttributes attrs) {
                info.files ++;

                String p = root.relativize (path).toString ();
                for (PathFilter f : ignoredFiles) {
                    if (f.hit (p)) {
                        if (logger.isTraceEnabled ()) {
                            logger.trace ("file {} is ignored.", p);
                        }
                        info.ignoredFiles.add (path);
                        return FileVisitResult.CONTINUE;
                    }
                }
                info.target.add (path);
                return FileVisitResult.CONTINUE;
            }
        });
        return info;
    }
}